package com.anteater.eattheant.view;

import java.util.List;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.util.Pair;

import com.anteater.eattheant.R;
import com.anteater.eattheant.view.ETASurfaceView.EndListener;
import com.anteater.eattheant.view.ETASurfaceView.SoundListener;
import com.anteater.eattheant.view.Tongue.MoveListener;

public class ETARenderer implements Renderer {

	private Maze maze;
	private Tongue tongue;
	private Anthill anthill;
	private Pair<Float, Float> nextMove;
	private EndListener endListener;
	private SoundListener soundListener;
	private boolean end;
	private Context context;
	private Mesh background;
	
	private short[][] graph;
	private List<com.anteater.eattheant.util.Ant> ants;
	private List<com.anteater.eattheant.util.Escape> escapes;
	
	public ETARenderer(short graph[][],
			List<com.anteater.eattheant.util.Ant> ants,
			List<com.anteater.eattheant.util.Escape> escapes,
			EndListener endListener, SoundListener soundListener,
			Context context) {
		this.graph = graph;
		this.ants = ants;
		this.escapes = escapes;
		this.context = context;
		this.endListener = endListener;
		this.soundListener = soundListener;
		end = false;
	}

	public void moveTongue(float x, float y) {
		nextMove = new Pair<Float, Float>(x / 2.5f, (y + 0.25f) / 2.5f);
	}

	public void releaseTongue() {
		this.soundListener.playTonqueSound();
		end = true;
	}

	private int loadTexture(GL10 gl, int file) {
		int texture[] = new int[1];

		gl.glGenTextures(1, texture, 0);

		Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
				file);

		gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]);

		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
				GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
				GL10.GL_NEAREST);

		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

		bitmap.recycle();

		return texture[0];
	}
	
	public void doStart(GL10 gl) {
		maze = new Maze(graph, escapes, gl, context);
		tongue = new Tongue(maze, gl, context);
		anthill = new Anthill(maze, ants, gl, context);
		
		background = new Rectangle(3, 6);
		background.setTexture(new float[] { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
				1.0f, 1.0f }, loadTexture(gl, R.drawable.play_bg_tex));

		tongue.setMoveListener(anthill.new TongueObserver(soundListener));
		tongue.setCollisionListener(new MoveListener() {
			@Override
			public void notify(short i, short j) {
				end = true;
			}
		});
	}
	
	private void doPhysics() {
		if (end) {
			if (tongue.release(anthill.getDeadAnts())) {
				doEnd();
			}
		} else {
			if (nextMove != null) {
				tongue.move(nextMove.first, nextMove.second);
				nextMove = null;
			}
			anthill.updateAnts();
		}
	}

	private void doDraw(GL10 gl) {
		gl.glPushMatrix();
		gl.glTranslatef(0.0f, -0.15f, 0.0f);
		background.draw(gl);
		gl.glPopMatrix();
		
		gl.glPushMatrix();

		gl.glTranslatef(0.0f, -0.25f, 0.0f);
		gl.glScalef(2.5f, 2.5f, 2.5f);
		maze.draw(gl);
		tongue.draw(gl);
		anthill.draw(gl);

		gl.glPopMatrix();
	}

	private void doEnd() {
		this.soundListener.stopTongueSound();
		endListener.notify(tongue.getScore());
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		doStart(gl);

		// Set the background color to black ( rgba ).
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);

		// Enable textures
		gl.glEnable(GL10.GL_TEXTURE_2D);
		
		// Enable Smooth Shading, default not really needed.
		gl.glShadeModel(GL10.GL_SMOOTH);

		// Depth buffer setup.
		gl.glClearDepthf(1.0f);

		// Enables depth testing.
		gl.glEnable(GL10.GL_DEPTH_TEST);

		// The type of depth testing to do.
		gl.glDepthFunc(GL10.GL_LEQUAL);

		// Really nice perspective calculations.
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
	}

	public void onDrawFrame(GL10 gl) {
		// Clears the screen and depth buffer.
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		// Replace the current matrix with the identity matrix
		gl.glLoadIdentity();

		// Translates 4 units into the screen.
		gl.glTranslatef(0.0f, 0.0f, -4.0f);

		doPhysics();
		doDraw(gl);
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		// Sets the current view port to the new size.
		gl.glViewport(0, 0, width, height);

		// Select the projection matrix
		gl.glMatrixMode(GL10.GL_PROJECTION);

		// Reset the projection matrix
		gl.glLoadIdentity();

		// Calculate the aspect ratio of the window
		GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f,
				100.0f);

		// Select the modelview matrix
		gl.glMatrixMode(GL10.GL_MODELVIEW);

		// Reset the modelview matrix
		gl.glLoadIdentity();
	}
}
